<template>
  <div class="favorite-panel">
    <a-spin
      :spinning="isNodeWidgetsLoading"
      class="spinning"
      :class="{ show: isNodeWidgetsLoading }"
    >
      <div
        v-if="!isNodeWidgetsLoading && allWidgets.length === 0"
        class="empty-status-container"
      >
        <img class="empty-img" :src="publicPath + '/img/empty.svg'" />
        <p>暂无可视化收藏</p>
      </div>
      <div v-show="allWidgets.length > 0 && !isNodeWidgetsLoading">
        <a-collapse
          v-model="activeKey"
          class="collapse"
          :bordered="false"
          expand-icon-position="right"
        >
          <a-collapse-panel
            v-for="(taskList, index) in allWidgets || []"
            :key="`${index}`"
            :header="taskList.taskName"
          >
            <div
              v-for="widget in taskList.widgets || []"
              :key="widget.id"
              class="list-item"
              :class="{
                square: [
                  widget.data.chartType,
                  widget.data.chartType[0],
                ].includes('scatterplot'),
              }"
            >
              <Chart
                class="chart"
                :chart-type="widget.data.chartType"
                :config="widget.data.chartOptions"
              />
              <div class="buttons">
                <!-- <a-tooltip placement="top">
                  <template slot="title">
                    <span>编辑</span>
                  </template>
                  <a-icon-font
                    class="icon"
                    type="iconicon-beifen6-copy-copy"
                    @click="onEdit(widget)"
                  />
                </a-tooltip> -->
                <a-tooltip placement="top">
                  <template slot="title">
                    <span>删除</span>
                  </template>
                  <a-icon-font
                    class="icon"
                    type="iconicon-beifen5"
                    @click="onDelete(widget)"
                  />
                </a-tooltip>
              </div>
              <div
                class="mask"
                title="点击放大"
                @click.stop="showModal(widget.data, taskList.taskName)"
              ></div>
              <!-- <div class="mask"></div>  -->
            </div>
          </a-collapse-panel>
        </a-collapse>
        <a-spin class="bottom-spinning" :spinning="btmLoadingVisible"></a-spin>
      </div>
      <visualization-preview-modal
        :visible="isModalVisible"
        :title="nodeName"
        :chart="modalChart"
        :show-save-btn="false"
        @close="isModalVisible = false"
      />
      <!-- <visualization-edit-modal
        :is-modal-visible="isModalShow"
        @ok="onConfirmEdit"
        @cancel="onCancel"
      /> -->
    </a-spin>
  </div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
import {
  getGlobalWidgets,
  deleteWidget,
  updateWidget,
  widgetQueryByTaskId,
  saveWidget,
} from '@/api/widget'
import Chart from '@/components/chart/chart.vue'
import {
  getAndDealWidgetData,
  removeNullEntries,
  parseBinCalculateData,
  ifMeetCityProvince,
  transferLabelKeyToString,
} from '@/util/recommend-visualization'
import { merge, throttle } from 'lodash'
import VisualizationEditModal from '@/components/studio/data/node-edit-panel/VisualizationEditModal.vue'
import VisualizationPreviewModal from '@/components/studio/data/node-edit-panel/VisualizationPreviewModal.vue'
import DataStore from '@/store/modules/dataview'
import { Widget, WidgetData } from '@/util/chart-widget'
import { publicPath } from '@/api/constants'

interface ResponseData {
  code: number
  success: boolean
  result: any
}

@Component({
  components: {
    Chart,
    VisualizationEditModal,
    VisualizationPreviewModal,
  },
})
export default class FavoritePanel extends Vue {
  // pipelineId 沿用版本1
  @Prop({ type: Number, default: 0 })
  pipelineId!: number
  private allWidgets: any = []
  private miscChartOptions = {
    size: [300, 200],
    labelIsShow: false,
    legendIsShow: false,
    padding: [10, 20, 10, 10],
    titleIsShow: false,
    yAxisTitleFontSize: 10,
    xAxisTitleFontSize: 10,
    axisTitleIsShow: true,

    xAxisIsShow: false,
    yAxisIsShow: false,
    axisGridIsShow: false,
    xAxisGridIsShow: false,
    yAxisGridIsShow: false,
    visibleItemsNumber: 16,
  }
  private activeKey: string[] = []
  private editingWidget: Widget | null = null
  private nodeWidgets: Widget[] = []
  private isNodeWidgetsLoading: boolean = false
  private startIndex: number = 0
  // 折叠板
  private scrollElement: any = null
  // 是否继续加载数据
  private isAvailable: boolean = true
  private btmLoadingVisible: boolean = false
  private loadWidgets: any = []
  private publicPath = publicPath

  private isModalVisible: boolean = false
  private modalChart: any = null
  //  modal title
  private nodeName: any = ''

  /**
   * pipeline 传值监听, pipeline 传入 跟新 pipeline
   */
  @Watch('pipelineId')
  public changePipelineId() {
    this.startIndex = 0
    if (this.favoriteStatus) {
      this.updatePipeline()
    }
  }

  @Watch('favoriteStatus')
  public favoriteStatusChange() {
    this.startIndex = 0
    if (this.favoriteStatus) {
      this.updatePipeline()
    }
  }

  @Watch('selectNode')
  private async onNodeChange() {
    // console.log('select node')
    this.allWidgets = []
    if (this.selectNode && this.favoriteStatus) {
      this.isNodeWidgetsLoading = true
      this.allWidgets = await this.getGlobalWidgets()
      this.isNodeWidgetsLoading = false
    }
  }

  @Watch('savingWidgetConfig')
  private onSavingWidgetConfigChange(widgetData: WidgetData) {
    this.onSave(widgetData)
  }

  public beforeMount() {
    this.startIndex = 0
    if (this.favoriteStatus) {
      this.updatePipeline()
    }
  }

  public mounted() {
    this.scrollElement = document.querySelector('.section-content')
    this.scrollElement.addEventListener(
      'scroll',
      throttle(() => this.onScroll(), 500)
    )
    this.scrollElement.scrollTop = 100
  }

  public beforeDestroy() {
    this.scrollElement.removeEventListener(
      'scroll',
      throttle(() => this.onScroll(), 500)
    )
  }

  private get selectNode() {
    return DataStore.selectNode
  }

  private get favoriteStatus() {
    return DataStore.getFavoriteStatus
  }

  private get projectId(): string {
    return (this.$route.params as any).id
  }

  public get isModalShow() {
    return DataStore.getVisualWindowShow
  }

  public get savingWidgetConfig() {
    return DataStore.savingWidgetConfig
  }

  public async onScroll() {
    const inner = this.scrollElement
    if (
      inner.scrollHeight - inner.scrollTop - 0.4 <= inner.clientHeight &&
      this.isAvailable
    ) {
      this.btmLoadingVisible = true
      this.startIndex += 5
      this.loadWidgets = await this.getGlobalWidgets()
      if (this.loadWidgets.length > 0) {
        // this.allWidgets.push(...this.loadWidgets)
        const element = this.allWidgets[this.allWidgets.length - 1]
        if (element.taskName === this.loadWidgets[0].taskName) {
          this.allWidgets[this.allWidgets.length - 1].widgets.push(
            ...this.loadWidgets[0].widgets
          )
          this.allWidgets.push(...this.loadWidgets.slice(1))
        } else {
          this.allWidgets.push(...this.loadWidgets)
        }
        this.activeKey = this.allWidgets
          ? Object.keys(Array.from({ length: this.allWidgets.length }))
          : []
      } else {
        this.isAvailable = false
        this.btmLoadingVisible = false
      }
    }
  }

  /**
   * 全局 更新 画布
   */
  public async updatePipeline() {
    this.isNodeWidgetsLoading = true
    this.allWidgets = await this.getGlobalWidgets()
    this.isNodeWidgetsLoading = false
  }

  /**
   * getWidgets ByTaskId
   */
  private async getWidgets(nodeData: any) {
    const response = await widgetQueryByTaskId({
      data: {
        tid: nodeData.tid,
        type: nodeData.type,
      },
    })
    const widgets: Widget[] = response.data?.result ?? []
    const promises = widgets.map(async (widget) => {
      const data = await this.getChartDataByWidget(widget.data)
      widget.data.chartOptions.value = data
      widget.data.chartOptions = {
        ...widget.data.chartOptions,
        ...this.miscChartOptions,
        ...this.getExtraChartOptions(widget.data),
      }
    })
    await Promise.all(promises)
    return widgets
  }

  /**
   * 获取全局可视化收藏列表
   */
  private async getGlobalWidgets() {
    let globalWidgets: any[] = []
    // this.allWidgets = []
    // if ( this.pipelineId != null) {
    //   const response = await getGlobalWidgets({
    //     data: {
    //       curPage:this.curPage,
    //       pageSize: 10,
    //       taskId: this.selectNode?.id?? '',
    //       pipelineId:this.pipelineId,
    //       projectId: this.projectId
    //     }
    //   })
    //   this.allWidgets = response.data?.result?.data?? []

    //   this.allWidgets.forEach(async(element: { widgets: any }) => {
    //     const widgets: Widget[] = element?.widgets ?? []
    //     const promises = widgets.map(async (widget) => {
    //       const data = await this.getChartDataByWidget(widget.data)
    //       widget.data.chartOptions.value = data
    //       widget.data.chartOptions = {
    //           ...widget.data.chartOptions,
    //           ...this.miscChartOptions,
    //           ...this.getExtraChartOptions(widget.data)
    //       }
    //     })
    //     await Promise.all(promises)
    //     console.log('all loaded')

    //     this.isNodeWidgetsLoading = false
    //   })

    //   if (this.allWidgets === [] || this.allWidgets === undefined || this.allWidgets.length === 0){
    //     this.isNodeWidgetsLoading = false
    //   }
    //   console.log('getGlobalWidgets',this.allWidgets)
    //   this.activeKey = this.allWidgets?Object.keys(Array.from({length:this.allWidgets.length})):[]
    if (this.pipelineId != null) {
      const response = await getGlobalWidgets({
        data: {
          // curPage:this.curPage,
          startIndex: this.selectNode?.id ? undefined : this.startIndex,
          pageSize: 5,
          taskId: this.selectNode?.id ?? '',
          pipelineId: this.pipelineId,
          projectId: this.projectId,
        },
      })
      globalWidgets = response.data?.result?.data ?? []
      if (this.selectNode?.id) {
        this.startIndex =
          response.data?.result?.totalElements ?? this.startIndex
      }
      this.isAvailable =
        this.startIndex < response.data?.result?.totalElements ?? false
      globalWidgets.forEach(async (element: { widgets: any }) => {
        const widgets: Widget[] = element?.widgets ?? []
        const promises = widgets.map(async (widget) => {
          let data = await this.getChartDataByWidget(widget.data)
          //  单列 - 地理信息 bin 操作后 不需要拼接
          if (
            widget.data.chartOptions.binCalculate &&
            !ifMeetCityProvince(widget.data.widgetJson.config.semantic)
          ) {
            data = parseBinCalculateData(
              data,
              widget.data.chartOptions.labelKey
            )
          }
          widget.data.chartOptions.value = data
          widget.data.chartOptions = {
            ...widget.data.chartOptions,
            ...this.miscChartOptions,
            ...this.getExtraChartOptions(widget.data),
          }
        })
        await Promise.all(promises)
        // console.log('loaded')
      })
      // console.log('all loaded')
      // this.refreshNodeWidgets(response.data.success, '界面初始化')

      if (this.activeKey.length === 0) {
        this.activeKey = globalWidgets
          ? Object.keys(Array.from({ length: globalWidgets.length }))
          : []
      }
    }

    return globalWidgets
  }

  private async getChartDataByWidget(widgetData: WidgetData) {
    const data = await getAndDealWidgetData(widgetData)
    // TODO: temp: remove null entries to avoid rendering error
    // return removeNullEntries(<any[]>data)
    //  处理labelKey 为 字符串，待chart断修复该问题，可移除该功能
    return transferLabelKeyToString(
      removeNullEntries(<any[]>data),
      widgetData.chartOptions.labelKey
    )
  }

  private getExtraChartOptions(chart: WidgetData) {
    const result: any = {}
    const { value } = chart.chartOptions

    // hack for 异常检测散点图配色
    if ([1, -1, '1', '-1'].includes(value?.[0]?.label)) {
      const abnormalIndex = value.findIndex((v: any) =>
        [-1, '-1'].includes(v.label)
      )
      if (abnormalIndex > 0) {
        result.value = [
          value[abnormalIndex],
          ...value.slice(0, abnormalIndex),
          ...value.slice(abnormalIndex + 1),
        ]
      }
    }
    // end of hack

    if (
      chart.chartType === 'scatterplot' ||
      chart.chartType[0] === 'scatterplot'
    ) {
      result.size = [300, 200]
      if (!Array.isArray(chart.chartOptions.pointRadius)) {
        result.pointRadius = 3
      }
    }

    return result
  }

  private async refreshNodeWidgets(success: boolean, name?: string) {
    if (success) {
      // TODO
      this.allWidgets = []
      this.isNodeWidgetsLoading = true
      this.startIndex = 0
      this.allWidgets = await this.getGlobalWidgets()
      this.isNodeWidgetsLoading = false
    }
    if (name) {
      if (success) {
        this.$message.success(`${name}成功`)
      } else {
        this.$message.error(`${name}失败`)
      }
    }
  }

  // private async refreshNodeWidgets(success: boolean, name?: string) {
  //   if (success) { // TODO
  //     this.nodeWidgets = await this.getWidgets()
  //   }
  //   if (name) {
  //     if (success) {
  //       this.$message.success(`${name}成功`)
  //     } else {
  //       this.$message.error(`${name}失败`)
  //     }
  //   }
  // }

  private async onDelete(widget: Widget) {
    const responseData = await this.deleteWidget(widget)
    this.refreshNodeWidgets(responseData.success, '删除可视化')
  }

  private async onEdit(widget: Widget) {
    const chartType = Array.isArray(widget.data.chartType)
      ? widget.data.chartType[1]
      : widget.data.chartType
    DataStore.setVisualWindowShow(true)
    DataStore.setOldItems({
      name: widget.name,
      chartType,
      chartOptions: {
        ...widget.data.chartOptions,
        // xAxisRenderMode: 'all', // 编辑模式下需要开启all模式（全部参数可控）
        // yAxisRenderMode: 'all',
        // xAxisGridIsShow: false,
        // yAxisGridIsShow: false,
      },
      widgetId: widget.id,
      dataId: widget.data.chartOptions.dataId,
      widgetJson: widget.data.widgetJson,
      formData: widget.data.formData,
    })
    this.editingWidget = widget
  }

  private async updateWidget(widget: Widget, newWidgetData: any) {
    const response = await updateWidget({
      data: {
        data: newWidgetData,
        id: widget.id,
        name: widget.name,
        // tid: this.selectNode.id,
        type: 'task',
        projectId: this.projectId,
      },
    })
    return response.data as ResponseData // {result: widgetId}
  }

  private async deleteWidget(widget: Widget) {
    const response = await deleteWidget({
      data: {
        id: widget.id,
        projectId: localStorage.getItem('projectId'),
        type: 'task',
      },
    })
    return response.data as ResponseData // {result: null}
  }

  private async onConfirmEdit(newWidgetData: WidgetData) {
    merge(newWidgetData.chartOptions, this.miscChartOptions)
    const responseData = await this.updateWidget(
      this.editingWidget!,
      newWidgetData
    )
    DataStore.setVisualWindowShow(false)
    this.editingWidget = null
    this.refreshNodeWidgets(responseData.success, '编辑可视化')
  }

  private onCancel() {
    DataStore.setVisualWindowShow(false)
  }

  private async onSave(widgetData: WidgetData) {
    //  暂时移除重复添加的校验
    // const widgetArray = this.allWidgets.flatMap(
    //   (item: { widgets: Widget[] }) => {
    //     return item.widgets
    //   }
    // )
    // const isAlreadySaved = widgetArray.some(
    //   (widget: { data: { formData: any } }) =>
    //     isEqual(widget.data.formData, widgetData.formData)
    // )
    // if (isAlreadySaved) {
    //   this.$message.error('不能重复添加可视化')
    //   return
    // }

    const responseData = await this.saveWidget(widgetData)
    this.refreshNodeWidgets(responseData.success, '添加可视化')
  }

  private async saveWidget(widgetData: WidgetData) {
    const response = await saveWidget({
      data: {
        dashboardId: -1,
        data: widgetData,
        name: Array.isArray(widgetData.chartType)
          ? widgetData.chartType[0]
          : widgetData.chartType,
        randomId: Math.floor(Math.random() * 10000),
        tid: this.selectNode.id,
        type: 'task',
        projectId: this.projectId,
      },
    })
    return response.data as ResponseData // {result: widgetId}
  }

  private showModal(chart: any, name: string) {
    this.isModalVisible = true
    this.modalChart = chart
    this.nodeName = name
  }
}
</script>
<style lang="less" scoped>
.favorite-panel {
  background: #fff;
  //   border-right: 1px solid #e9e9e9;
  height: 100%;
  width: 360px;

  .spinning {
    &.show {
      margin-top: 50px;
    }
  }

  .bottom-spinning {
    width: 100%;
  }

  .empty-status-container {
    align-items: center;
    display: flex;
    flex-direction: column;
    height: 100%;
    justify-content: center;
    margin-top: 50px;

    .empty-img {
      margin-top: 15px;
    }
  }

  /deep/ .collapse {
    background-color: #fff;
    border: 0;

    .ant-collapse-header {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 100%;
    }
  }

  .list-item {
    border: 1px solid #d9d9d9;
    flex: 0 0 164px;
    margin-bottom: 10px;
    min-height: 212px;
    position: relative;

    .buttons {
      align-items: center;
      display: flex;
      // flex-direction: row;
      position: absolute;
      right: 30px;
      top: 12px;
      // transform: translateY(-50%);
      // visibility: hidden;
      width: 12px;
      z-index: 300;

      .icon {
        color: #5d637e;
        cursor: pointer;
        font-size: 15px;
        padding: 0 8px;
        // margin-right: 10px;

        &:hover {
          color: #6973ff;
        }
      }
    }

    .mask {
      background: transparent;
      bottom: 0;
      cursor: pointer;
      height: calc(100% - 30px);
      left: 0;
      position: absolute;
      width: 100%;
      z-index: 300;
    }

    &:hover .buttons {
      visibility: visible;
    }

    &.more {
      cursor: pointer;
      line-height: 164px;
      text-align: center;
    }

    &.square {
      flex: 0 0 300px;
    }
  }
}
</style>
